package com.azt.front.action.freemarker;

import freemarker.core.Environment;
import freemarker.template.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.support.RequestContext;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static freemarker.template.ObjectWrapper.DEFAULT_WRAPPER;
import static org.springframework.web.servlet.view.AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE;

/**
 * Freemarker标签工具类
 * 
 */
public abstract class DirectiveUtils {
	/**
	 * 输出参数：对象数据
	 */
	public static final String OUT_BEAN = "tag_bean";
	/**
	 * 输出参数：列表数据
	 */
	public static final String OUT_LIST = "tag_list";
	/**
	 * 输出参数：分页数据
	 */
	public static final String OUT_PAGINATION = "tag_pagination";

	/**
	 * 将params的值复制到variable中
	 * 
	 * @param env
	 * @param params
	 * @return 原Variable中的值
	 * @throws TemplateException
	 */
	public static Map<String, TemplateModel> addParamsToVariable(Environment env, Map<String, TemplateModel> params) throws TemplateException {
		Map<String, TemplateModel> origMap = new HashMap<String, TemplateModel>();
		if (params.size() <= 0) {
			return origMap;
		}
		Set<Map.Entry<String, TemplateModel>> entrySet = params.entrySet();
		String key;
		TemplateModel value;
		for (Map.Entry<String, TemplateModel> entry : entrySet) {
			key = entry.getKey();
			value = env.getVariable(key);
			if (value != null) {
				origMap.put(key, value);
			}
			env.setVariable(key, entry.getValue());
		}
		return origMap;
	}

	/**
	 * 将variable中的params值移除
	 * 
	 * @param env
	 * @param params
	 * @param origMap
	 * @throws TemplateException
	 */
	public static void removeParamsFromVariable(Environment env, Map<String, TemplateModel> params, Map<String, TemplateModel> origMap) throws TemplateException {
		if (params.size() <= 0) {
			return;
		}
		for (String key : params.keySet()) {
			env.setVariable(key, origMap.get(key));
		}
	}

	/**
	 * 获得RequestContext
	 * 
	 * ViewResolver中的exposeSpringMacroHelpers必须为true
	 * 
	 * @param env
	 * @return
	 * @throws TemplateException
	 */
	public static RequestContext getContext(Environment env) throws TemplateException {
		TemplateModel ctx = env.getGlobalVariable(SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE);
		if (ctx instanceof AdapterTemplateModel) {
			return (RequestContext) ((AdapterTemplateModel) ctx).getAdaptedObject(RequestContext.class);
		} else {
			throw new TemplateModelException("RequestContext '" + SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE + "' not found in DataModel.");
		}
	}

	public static String getString(String name, Map<String, TemplateModel> params) throws TemplateException {
		return getString(name, params, null);
	}

	public static String getString(String name, Map<String, TemplateModel> params, String def) throws TemplateException {
		TemplateModel model = params.get(name);
		if (model == null) {
			return def;
		}
		if (model instanceof TemplateScalarModel) {
			return ((TemplateScalarModel) model).getAsString();
		} else if ((model instanceof TemplateNumberModel)) {
			return ((TemplateNumberModel) model).getAsNumber().toString();
		} else {
			throw new MustStringException(name);
		}
	}

	public static String[] getStringArray(String name, Map<String, TemplateModel> params) throws TemplateException {
		String str = DirectiveUtils.getString(name, params);
		if (StringUtils.isBlank(str)) {
			return null;
		}
		String[] arr = StringUtils.split(str, ',');
		return arr;
	}

	public static Long getLong(String name, Map<String, TemplateModel> params) throws TemplateException {
		TemplateModel model = params.get(name);
		if (model == null) {
			return null;
		}
		if (model instanceof TemplateScalarModel) {
			String s = ((TemplateScalarModel) model).getAsString();
			if (StringUtils.isBlank(s)) {
				return null;
			}
			try {
				return Long.parseLong(s);
			} catch (NumberFormatException e) {
				throw new MustNumberException(name);
			}
		} else if (model instanceof TemplateNumberModel) {
			return ((TemplateNumberModel) model).getAsNumber().longValue();
		} else {
			throw new MustNumberException(name);
		}
	}

	public static Integer getInt(String name, Map<String, TemplateModel> params) throws TemplateException {
		return getInt(name, params, null);
	}

	public static Integer getInt(String name, Map<String, TemplateModel> params, Integer def) throws TemplateException {
		TemplateModel model = params.get(name);
		if (model == null) {
			return def;
		}
		if (model instanceof TemplateScalarModel) {
			String s = ((TemplateScalarModel) model).getAsString();
			if (StringUtils.isBlank(s)) {
				return def;
			}
			try {
				return Integer.parseInt(s);
			} catch (NumberFormatException e) {
				throw new MustNumberException(name);
			}
		} else if (model instanceof TemplateNumberModel) {
			return ((TemplateNumberModel) model).getAsNumber().intValue();
		} else {
			throw new MustNumberException(name);
		}
	}

	public static Short getShort(String name, Map<String, TemplateModel> params) throws TemplateException {
		TemplateModel model = params.get(name);
		if (model == null) {
			return null;
		}
		if (model instanceof TemplateScalarModel) {
			String s = ((TemplateScalarModel) model).getAsString();
			if (StringUtils.isBlank(s)) {
				return null;
			}
			try {
				return Short.parseShort(s);
			} catch (NumberFormatException e) {
				throw new MustNumberException(name);
			}
		} else if (model instanceof TemplateNumberModel) {
			return ((TemplateNumberModel) model).getAsNumber().shortValue();
		} else {
			throw new MustNumberException(name);
		}
	}

	public static Integer[] getIntArray(String name, Map<String, TemplateModel> params) throws TemplateException {
		String str = DirectiveUtils.getString(name, params);
		if (StringUtils.isBlank(str)) {
			return null;
		}
		String[] arr = StringUtils.split(str, ',');
		Integer[] ids = new Integer[arr.length];
		int i = 0;
		try {
			for (String s : arr) {
				ids[i++] = Integer.valueOf(s);
			}
			return ids;
		} catch (NumberFormatException e) {
			throw new MustSplitNumberException(name, e);
		}
	}

	public static Boolean getBool(String name, Map<String, TemplateModel> params) throws TemplateException {
		TemplateModel model = params.get(name);
		if (model == null) {
			return null;
		}
		if (model instanceof TemplateBooleanModel) {
			return ((TemplateBooleanModel) model).getAsBoolean();
		} else if (model instanceof TemplateNumberModel) {
			return !(((TemplateNumberModel) model).getAsNumber().intValue() == 0);
		} else if (model instanceof TemplateScalarModel) {
			String s = ((TemplateScalarModel) model).getAsString();
			// 空串应该返回null还是true呢？
			if (!StringUtils.isBlank(s)) {
				return !(s.equals("0") || s.equalsIgnoreCase("false") || s.equalsIgnoreCase("f"));
			} else {
				return null;
			}
		} else {
			throw new MustBooleanException(name);
		}
	}

	public static boolean getBool(String name, Map<String, TemplateModel> params, boolean def) {
		try {
			Boolean b = getBool(name, params);
			return b == null ? def : b;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return def;
	}

	public static void writer(String name, Object obj, Environment env, Map params, TemplateDirectiveBody body) throws TemplateException, IOException {

		// 该map 用来临时保存 参数
		Map<String, TemplateModel> paramWrap = new HashMap<String, TemplateModel>(params);

		// 第二个参数为freemarket提供的包装器，用来将java对象转成 freemarket所支持的 模型 TemplateModel
		paramWrap.put(name, DEFAULT_WRAPPER.wrap(obj));

		// 如果自定义的参数key值，与环境变里的key相同，则先将env里同名的移动到
		// 下面的map。用来临时存放，渲染页面后，再将其还原到env里,并把上面map里的参数复制到env里
		Map<String, TemplateModel> origMap = DirectiveUtils.addParamsToVariable(env, paramWrap);

		// 开始渲染页面
		body.render(env.getOut());

		// 还原env里原有的参数
		DirectiveUtils.removeParamsFromVariable(env, paramWrap, origMap);
	}

	
	/** 
	 * @Title: writer 
	 * @Description: 封装多个参数方法
	 * @param parmap
	 * @param env
	 * @param params
	 * @param body
	 * @throws TemplateException
	 * @throws IOException
	 * @return: void
	 * @author: 查恒 2016年9月5日 下午3:06:19 
	 */
	public static void writer(Map<String, Object> parmap,  Environment env, Map params, TemplateDirectiveBody body) throws TemplateException, IOException {

		// 该map 用来临时保存 参数
		Map<String, TemplateModel> paramWrap = new HashMap<String, TemplateModel>(params);

		// 第二个参数为freemarket提供的包装器，用来将java对象转成 freemarket所支持的 模型 TemplateModel
		
		for(Map.Entry<String, Object> entry:parmap.entrySet()){   
			paramWrap.put(entry.getKey(), DEFAULT_WRAPPER.wrap(entry.getValue()));
		}  

		// 如果自定义的参数key值，与环境变里的key相同，则先将env里同名的移动到
		// 下面的map。用来临时存放，渲染页面后，再将其还原到env里,并把上面map里的参数复制到env里
		Map<String, TemplateModel> origMap = DirectiveUtils.addParamsToVariable(env, paramWrap);

		// 开始渲染页面
		body.render(env.getOut());

		// 还原env里原有的参数
		DirectiveUtils.removeParamsFromVariable(env, paramWrap, origMap);
	}
	
	
	public static void writerBean(Object obj, Environment env, Map params, TemplateDirectiveBody body) throws TemplateException, IOException {
		DirectiveUtils.writer(DirectiveUtils.OUT_BEAN, obj, env, params, body);
	}

	public static void writerList(Object obj, Environment env, Map params, TemplateDirectiveBody body) throws TemplateException, IOException {
		DirectiveUtils.writer(DirectiveUtils.OUT_LIST, obj, env, params, body);
	}

}

// 自定义 freemarker Exception

class MustBooleanException extends TemplateModelException {
	private static final long serialVersionUID = -5628768036275974861L;

	public MustBooleanException(String paramName) {
		super("The \"" + paramName + "\" parameter must be a boolean.");
	}
}

class MustDateException extends TemplateModelException {
	private static final long serialVersionUID = 7008543624121934176L;

	public MustDateException(String paramName) {
		super("The \"" + paramName + "\" parameter must be a date.");
	}
}

class MustNumberException extends TemplateModelException {
	private static final long serialVersionUID = -3800849706103273579L;

	public MustNumberException(String paramName) {
		super("The \"" + paramName + "\" parameter must be a number.");
	}
}

class MustSplitNumberException extends TemplateModelException {
	private static final long serialVersionUID = 6793026511777063085L;

	public MustSplitNumberException(String paramName) {
		super("The \"" + paramName + "\" parameter must be a number split by ','");
	}

	public MustSplitNumberException(String paramName, Exception cause) {
		super("The \"" + paramName + "\" parameter must be a number split by ','", cause);
	}
}

class MustStringException extends TemplateModelException {
	private static final long serialVersionUID = 2247656497522279089L;

	public MustStringException(String paramName) {
		super("The \"" + paramName + "\" parameter must be a string.");
	}
}
